using System;
using BSDS.Commands.Commands.Creation;
using BSDS.Core.Infrastructure.Logging;

namespace BSDS.Commands.Commands
{
    public class CommandDispatcher
    {
        private readonly CommandFactory commandFactory;
        private readonly Logger logger;

        public event EventHandler Dispatching;
        public event EventHandler<CommandDispatchEventArgs> Dispatched;

        public CommandDispatcher(CommandFactory commandFactory, Logger logger)
        {
            this.commandFactory = commandFactory;
            this.logger = logger;
        }

        public void Dispatch(CommandInfo commandInfo)
        {
            OnDispatching();

            logger.Log("Dispatching command: " + commandInfo);

            var context = new ExecutionContext(commandInfo, this);

            var commandResult = CommandResult.Success();
            var command = commandFactory.Create(commandInfo.Parameters);

            try
            {
                if (command == null)
                {
                    throw new ArgumentException(string.Format("Unrecognized command: {0}.", commandInfo));
                }

                commandResult = command.Execute(context);
            }
            catch (Exception ex)
            {
                commandResult = CommandResult.Error(ex);
                
                logger.Log(string.Format("Error: \n{0}", ex));

                throw;
            }
            finally
            {
                OnDispatched(command, commandResult, context);
            }
        }

        private void OnDispatched(ICommand command, CommandResult result,
                                  ExecutionContext context)
        {
            if (Dispatched != null)
            {
                Dispatched(this, new CommandDispatchEventArgs(command, result, context));
            }
        }

        private void OnDispatching()
        {
            if (Dispatching != null)
            {
                Dispatching(this, EventArgs.Empty);
            }
        }
    }
}